package edu.hawaii.halealohacli.command;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
import java.util.Locale;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * This is a command module for executing the rank-tower command.
 * 
 * @author Sergey Negrashov
 */
public class RankTowers implements Command {

  /**
   * Name of this module.
   */
  static final String MODULE_NAME = "rank-towers";

  /**
   * Usage for this module.
   */
  static final String MODULE_USAGE = MODULE_NAME
      + ":  [start date] [end date]\nReturns a list in sorted order from least to most energy"
      + " consumed between the [start] and [end] date (yyyy-mm-dd).\n\n";

  /**
   * Client to use with this command.
   */
  WattDepotClient client;

  /**
   * Start time.
   */
  XMLGregorianCalendar startDate;

  /**
   * End time.
   */
  XMLGregorianCalendar endDate;

  /**
   * First argument as passed to the module.
   */
  String startDateString;

  /**
   * Second argument as passed to the module.
   */
  String endDateString;

  /**
   * A flag to check if the arguments and client are ok.
   */
  boolean ok;

  /**
   * Error string.
   */
  String error = "Unknown error";

  /**
   * Towers to report for.
   */
  static final String towers[] = { "Mokihana", "Ilima", "Lehua", "Lokelani" };

  @Override
  public void initialize(String[] args, WattDepotClient client) {
    ok = true;
    this.client = client;
    if (args.length != 2) {
      error = "Invalid arguments for " + MODULE_NAME + ".\n" + MODULE_USAGE;
      ok = false;
      return;
    }
    startDateString = args[0];
    endDateString = args[1];
    try {
      this.startDate = Tstamp.makeTimestamp(startDateString);
    }
    catch (Exception e) {
      ok = false;
      error = "Argument \"" + startDateString + "\" is invalid.";
      return;
    }
    try {
      this.endDate = Tstamp.makeTimestamp(endDateString);
    }
    catch (Exception e) {
      ok = false;
      error = "Argument \"" + endDateString + "\" is invalid.";
      return;
    }
    if (Tstamp.greaterThan(startDate, endDate) || Tstamp.equal(startDate, endDate)) {
      ok = false;
      error = "End date must be greater than start date.";
      return;
    }

    Calendar cal = Calendar.getInstance(Locale.US);
    XMLGregorianCalendar today = Tstamp.makeTimestamp(cal.getTimeInMillis());
    if (Tstamp.greaterThan(endDate, today)) {
      ok = false;
      error = "The end date cannot be in the future.";
      return;
    }

  }

  @Override
  public void execute() {
    System.out.println(printSorted());
  }

  @Override
  public String getCommandName() {
    return MODULE_NAME;
  }

  @Override
  public String getCommandUsage() {
    return MODULE_USAGE;
  }

  /**
   * Prints the energy consumed between the provided timestamps in descending order.
   * 
   * @return a formated table of energy consumption in a string.
   * 
   */
  public String printSorted() {
    if (!ok) {
      return error;
    }
    if (client == null || !client.isHealthy()) {
      return "Could not connect to the server.";
    }
    try {
      List<SortingObject<Double>> energyList = getTowerEnergyList();
      StringBuilder sb = new StringBuilder();
      Formatter formatter = new Formatter(sb, Locale.US);
      if (energyList.isEmpty()) {
        return formatter.toString();
      }
      Collections.sort(energyList);
      formatter.format("%s\n", "For the interval " + startDateString + " to " + endDateString
          + ", energy consumption by tower was:");
      for (SortingObject<Double> tower : energyList) {
        if (tower.key == -1.0) {
          formatter.format("%1$-20s\t\tNot authorized\n", tower.value);
        }
        else if (tower.key == -2.0) {
          formatter.format("%1$-20s\t\tNo such source\n", tower.value);
        }
        else {
          formatter.format("%1$-20s\t\t %2$.0f kWh\n", tower.value, tower.key / 1000);
        }
      }
      return formatter.toString();
    }
    catch (BadXmlException e) {
      return "The tower ranking could not be retrieved for the dates given.";
    }
    catch (MiscClientException e) {
      return "The tower ranking could not be retrieved.";
    }
  }

  /**
   * List of energy use with the consuming source. Only takes into account virtual sources.
   * 
   * @return list of SortingObjects with energy consumed and source.
   * @throws BadXmlException If the XML string is malformed there is nothing we can do here we must
   * handle it in the execute method since the most probable cause is the a malformed timestamp.
   * @throws MiscClientException Unknown exception is handled in the <code>execute</code> method.
   */
  public List<SortingObject<Double>> getTowerEnergyList() throws BadXmlException,
      MiscClientException {
    List<SortingObject<Double>> energyList = new ArrayList<SortingObject<Double>>();
    for (String s : towers) {
      Double towerEnergy;
      try {
        towerEnergy = client.getEnergyConsumed(s, startDate, endDate, 60);
        energyList.add(new SortingObject<Double>(towerEnergy, s));
      }
      catch (NotAuthorizedException e) {
        energyList.add(new SortingObject<Double>(-1.0, s));
      }
      catch (ResourceNotFoundException e) {
        energyList.add(new SortingObject<Double>(-2.0, s));
      }
    }
    return energyList;
  }

}
